package com.todolist.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.*;
import com.todolist.client.event.DeleteProjectEvent;
import com.todolist.client.service.ProjectService;
import com.todolist.client.service.ProjectServiceAsync;
import com.todolist.client.service.TaskService;
import com.todolist.client.service.TaskServiceAsync;
import com.todolist.shared.domain.Project;
import com.todolist.shared.domain.Task;

import java.util.ArrayList;
import java.util.List;

public class ToDoListGridWidget extends Composite {
    private static final String SAVE_PROJECT_BUTTON_IMAGE = "saveProjectButton.png";
    private static final String EDIT_PROJECT_BUTTON_IMAGE = "editProjectButton.png";
    private static final String DELETE_PROJECT_BUTTON_IMAGE = "deleteProjectButton.png";
    private static final String ADD_TASK_PLUS_BUTTON_IMAGE = "addTaskPlusButton.png";
    private static final String ADD_TASK_BUTTON_IMAGE = "addTaskButton.png";
    private static final String INCREASE_PRIORITY_BUTTON_IMAGE = "increasePriorityButton.png";
    private static final String DECREASE_PRIORITY_BUTTON_IMAGE = "decreasePriorityButton.png";
    private static final String EDIT_TASK_BUTTON_IMAGE = "editTaskButton.png";
    private static final String SAVE_TASK_BUTTON_IMAGE = "saveTaskButton.png";
    private static final String DELETE_TASK_BUTTON_IMAGE = "deleteTaskButton.png";

    private static final String BUTTON_STYLE = "button";
    private static final String COMPLETED_TASK_STYLE = "completedTask";
    private static final String TASK_TEXTBOX_STYLE = "taskTextBox";
    private static final String CHECKBOX_COLUMN_STYLE = "checkBoxColumn";
    private static final String TEXTBOX_COLUMN_STYLE = "textBoxColumn";

    interface ToDoListGridWidgetUiBinder extends UiBinder<Widget, ToDoListGridWidget> {
    }

    private static ToDoListGridWidgetUiBinder uiBinder = GWT.create(ToDoListGridWidgetUiBinder.class);

    private ProjectServiceAsync projectService = GWT.create(ProjectService.class);
    private TaskServiceAsync taskService = GWT.create(TaskService.class);

    private SimpleEventBus eventBus = null;
    private Project project = null;
    private String oldProjectName = null;

    @UiField
    TextBox projectNameTextBox;
    @UiField
    Label projectNameLabel;
    @UiField(provided = true)
    PushButton saveProjectButton;
    @UiField(provided = true)
    PushButton editProjectButton;
    @UiField(provided = true)
    PushButton deleteProjectButton;
    @UiField(provided = true)
    PushButton addTaskPlusButton;
    @UiField
    TextBox addTaskTextBox;
    @UiField(provided = true)
    PushButton addTaskButton;
    @UiField
    FlexTable tasksTable;

    public ToDoListGridWidget(SimpleEventBus eventBus) {
        this.eventBus = eventBus;

        initProvidedWidgets();
        initWidget(uiBinder.createAndBindUi(this));
    }

    public ToDoListGridWidget(SimpleEventBus eventBus, Project project) {
        this.eventBus = eventBus;
        this.project = project;

        initProvidedWidgets();
        initWidget(uiBinder.createAndBindUi(this));
        initWidgets();

        refreshProjectTasks(project);
    }

    public void setProject(Project project) {
        this.project = project;
    }

    public Project getProject() {
        return project;
    }

    @UiHandler("saveProjectButton")
    void handleSaveProjectButtonClick(ClickEvent clickEvent) {
        projectNameLabel.setText(projectNameTextBox.getText());
        projectNameLabel.setVisible(true);
        projectNameTextBox.setVisible(false);

        saveProjectButton.setVisible(false);
        editProjectButton.setVisible(true);

        if (oldProjectName != null) {
            projectService.updateProjectName(oldProjectName, projectNameLabel.getText(), new AsyncCallback<Void>() {
                @Override
                public void onFailure(Throwable throwable) {
                    Window.alert("Cannot update project name!");
                }

                @Override
                public void onSuccess(Void aVoid) {
                    project.setName(projectNameLabel.getText());
                }
            });
        } else {
            projectService.getProject(projectNameLabel.getText(), new AsyncCallback<Project>() {
                @Override
                public void onFailure(Throwable throwable) {
                    Window.alert("Cannot get the project!");
                }

                @Override
                public void onSuccess(Project project) {
                    if (project == null) {
                        projectService.addProject(projectNameLabel.getText(), new AsyncCallback<Void>() {
                            @Override
                            public void onFailure(Throwable throwable) {
                                Window.alert("Cannot add the project!");
                            }

                            @Override
                            public void onSuccess(Void aVoid) {
                                projectService.getProject(projectNameLabel.getText(), new AsyncCallback<Project>() {
                                    @Override
                                    public void onFailure(Throwable throwable) {
                                        Window.alert("Cannot get the project!");
                                    }

                                    @Override
                                    public void onSuccess(Project result) {
                                        setProject(result);
                                        enableTaskWidgets(result != null);
                                    }
                                });
                            }
                        });
                    }
                }
            });
        }

        oldProjectName = null;
    }

    @UiHandler("editProjectButton")
    void handleEditProjectButtonClick(ClickEvent clickEvent) {
        oldProjectName = project.getName();

        projectNameTextBox.setText(oldProjectName);
        projectNameTextBox.setVisible(true);
        projectNameLabel.setVisible(false);

        saveProjectButton.setVisible(true);
        editProjectButton.setVisible(false);
    }

    @UiHandler("deleteProjectButton")
    void handleDeleteProjectButtonClick(ClickEvent clickEvent) {
        if (project == null) {
            fireDeleteProjectEvent();
        } else {
            taskService.deleteTasks(project.getId(), new AsyncCallback<Void>() {
                @Override
                public void onFailure(Throwable throwable) {
                    Window.alert("Cannot delete the tasks!");
                }

                @Override
                public void onSuccess(Void aVoid) {
                    projectService.deleteProject(projectNameLabel.getText(), new AsyncCallback<Void>() {
                        @Override
                        public void onFailure(Throwable throwable) {
                            Window.alert("Cannot delete the project!");
                        }

                        @Override
                        public void onSuccess(Void aVoid) {
                            fireDeleteProjectEvent();
                        }
                    });
                }
            });
        }
    }

    @UiHandler("addTaskPlusButton")
    void handleAddTaskPlusButtonClick(ClickEvent clickEvent) {
        addTask();
    }

    @UiHandler("addTaskButton")
    void handleAddTaskButtonClick(ClickEvent clickEvent) {
        addTask();
    }

    private void initProvidedWidgets() {
        saveProjectButton = new PushButton(new Image(SAVE_PROJECT_BUTTON_IMAGE));
        saveProjectButton.setStyleName(BUTTON_STYLE);

        editProjectButton = new PushButton(new Image(EDIT_PROJECT_BUTTON_IMAGE));
        editProjectButton.setStyleName(BUTTON_STYLE);

        deleteProjectButton = new PushButton(new Image(DELETE_PROJECT_BUTTON_IMAGE));
        deleteProjectButton.setStyleName(BUTTON_STYLE);

        addTaskPlusButton = new PushButton(new Image(ADD_TASK_PLUS_BUTTON_IMAGE));
        addTaskPlusButton.setStyleName(BUTTON_STYLE);

        addTaskButton = new PushButton(new Image(ADD_TASK_BUTTON_IMAGE));
        addTaskButton.setStyleName(BUTTON_STYLE);
    }

    private void initWidgets() {
        projectNameLabel.setText(project.getName());
        projectNameLabel.setVisible(true);
        projectNameTextBox.setVisible(false);

        saveProjectButton.setVisible(false);
        editProjectButton.setVisible(true);

        enableTaskWidgets(project != null);
    }

    private void addTask() {
        taskService.getMaxTaskPriority(new AsyncCallback<Integer>() {
            @Override
            public void onFailure(Throwable throwable) {
                Window.alert("Cannot get max task priority!");
            }

            @Override
            public void onSuccess(Integer result) {
                taskService.addTask(project.getId(), addTaskTextBox.getText(), result != null ? result + 1 : 0,
                        new AsyncCallback<Void>() {
                            @Override
                            public void onFailure(Throwable throwable) {
                                Window.alert("Cannot add the task!");
                            }

                            @Override
                            public void onSuccess(Void aVoid) {
                                addTaskTextBox.setText("");

                                refreshProjectTasks(project);
                            }
                        });
            }
        });
    }

    private void enableTaskWidgets(boolean enable) {
        addTaskTextBox.setEnabled(enable);
        addTaskPlusButton.setEnabled(enable);
        addTaskButton.setEnabled(enable);
    }

    private void refreshProjectTasks(Project project) {
        taskService.getTasks(project.getId(), new AsyncCallback<ArrayList<Task>>() {
            @Override
            public void onFailure(Throwable throwable) {
                Window.alert("Cannot get the tasks!");
            }

            @Override
            public void onSuccess(ArrayList<Task> tasks) {
                fillTasksTable(tasks);
            }
        });

    }

    private void fillTasksTable(final List<Task> tasks) {
        tasksTable.removeAllRows();

        for (final Task task : tasks) {
            final int row = tasksTable.getRowCount();

            final CheckBox completedCheckBox = new CheckBox();
            completedCheckBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
                @Override
                public void onValueChange(ValueChangeEvent<Boolean> booleanValueChangeEvent) {
                    taskService.updateTaskCompleteness(task.getText(), completedCheckBox.getValue(),
                            new AsyncCallback<Void>() {
                                @Override
                                public void onFailure(Throwable throwable) {

                                }

                                @Override
                                public void onSuccess(Void aVoid) {
                                    HTMLTable.CellFormatter cellFormatter = tasksTable.getCellFormatter();

                                    if (completedCheckBox.getValue()) {
                                        cellFormatter.addStyleName(row, 1, COMPLETED_TASK_STYLE);
                                    } else {
                                        cellFormatter.removeStyleName(row, 1, COMPLETED_TASK_STYLE);
                                    }
                                }
                            });
                }
            });

            completedCheckBox.setValue(task.getCompleted());

            PushButton increasePriorityButton = new PushButton(new Image(INCREASE_PRIORITY_BUTTON_IMAGE));
            increasePriorityButton.setStyleName(BUTTON_STYLE);
            increasePriorityButton.setEnabled(row != 0);
            increasePriorityButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent clickEvent) {
                    Task higherTask = tasks.get(row - 1);
                    Integer higherTaskPriority = higherTask.getPriority();

                    higherTask.setPriority(task.getPriority());
                    task.setPriority(higherTaskPriority);

                    taskService.updateTaskPriority(higherTask.getText(), higherTask.getPriority(),
                            new AsyncCallback<Void>() {
                                @Override
                                public void onFailure(Throwable throwable) {
                                    Window.alert("Cannot decrease task priority!");
                                }

                                @Override
                                public void onSuccess(Void aVoid) {
                                    taskService.updateTaskPriority(task.getText(), task.getPriority(),
                                            new AsyncCallback<Void>() {
                                                @Override
                                                public void onFailure(Throwable throwable) {
                                                    Window.alert("Cannot increase task priority!");
                                                }

                                                @Override
                                                public void onSuccess(Void aVoid) {
                                                    refreshProjectTasks(project);
                                                }
                                            }
                                    );
                                }
                            });
                }
            });

            PushButton decreasePriorityButton = new PushButton(new Image(DECREASE_PRIORITY_BUTTON_IMAGE));
            decreasePriorityButton.setStyleName(BUTTON_STYLE);
            decreasePriorityButton.setEnabled(row < tasks.size() - 1);
            decreasePriorityButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent clickEvent) {
                    Task lowerTask = tasks.get(row + 1);
                    Integer higherTaskPriority = lowerTask.getPriority();

                    lowerTask.setPriority(task.getPriority());
                    task.setPriority(higherTaskPriority);

                    taskService.updateTaskPriority(lowerTask.getText(), lowerTask.getPriority(),
                            new AsyncCallback<Void>() {
                                @Override
                                public void onFailure(Throwable throwable) {
                                    Window.alert("Cannot increase task priority!");
                                }

                                @Override
                                public void onSuccess(Void aVoid) {
                                    taskService.updateTaskPriority(task.getText(), task.getPriority(),
                                            new AsyncCallback<Void>() {
                                                @Override
                                                public void onFailure(Throwable throwable) {
                                                    Window.alert("Cannot decrease task priority!");
                                                }

                                                @Override
                                                public void onSuccess(Void aVoid) {
                                                    refreshProjectTasks(project);
                                                }
                                            }
                                    );
                                }
                            });
                }
            });

            final TextBox editTaskTextBox = new TextBox();
            editTaskTextBox.addStyleName(TASK_TEXTBOX_STYLE);

            final PushButton editTaskButton = new PushButton(new Image(EDIT_TASK_BUTTON_IMAGE));
            editTaskButton.setStyleName(BUTTON_STYLE);

            final PushButton saveTaskButton = new PushButton(new Image(SAVE_TASK_BUTTON_IMAGE));
            saveTaskButton.setStyleName(BUTTON_STYLE);

            editTaskButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent clickEvent) {
                    editTaskTextBox.setText(task.getText());

                    tasksTable.setWidget(row, 1, editTaskTextBox);
                    tasksTable.setWidget(row, 4, saveTaskButton);
                }
            });

            saveTaskButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent clickEvent) {
                    taskService.updateTaskText(task.getText(), editTaskTextBox.getText(), new AsyncCallback<Void>() {
                        @Override
                        public void onFailure(Throwable throwable) {
                            Window.alert("Cannot update task text!");
                        }

                        @Override
                        public void onSuccess(Void aVoid) {
                            task.setText(editTaskTextBox.getText());

                            tasksTable.setText(row, 1, editTaskTextBox.getText());
                            tasksTable.setWidget(row, 4, editTaskButton);
                        }
                    });
                }
            });

            PushButton deleteTaskButton = new PushButton(new Image(DELETE_TASK_BUTTON_IMAGE));
            deleteTaskButton.setStyleName(BUTTON_STYLE);

            deleteTaskButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent clickEvent) {
                    taskService.deleteTask(project.getId(), task.getText(), new AsyncCallback<Void>() {
                        @Override
                        public void onFailure(Throwable throwable) {
                            Window.alert("Cannot delete the task!");
                        }

                        @Override
                        public void onSuccess(Void aVoid) {
                            refreshProjectTasks(project);
                        }
                    });
                }
            });

            tasksTable.setWidget(row, 0, completedCheckBox);
            tasksTable.setText(row, 1, task.getText());
            tasksTable.setWidget(row, 2, increasePriorityButton);
            tasksTable.setWidget(row, 3, decreasePriorityButton);
            tasksTable.setWidget(row, 4, editTaskButton);
            tasksTable.setWidget(row, 5, deleteTaskButton);

            HTMLTable.CellFormatter cellFormatter = tasksTable.getCellFormatter();
            cellFormatter.addStyleName(row, 0, CHECKBOX_COLUMN_STYLE);
            cellFormatter.addStyleName(row, 1, TEXTBOX_COLUMN_STYLE);

            if (task.getCompleted()) {
                cellFormatter.addStyleName(row, 1, COMPLETED_TASK_STYLE);
            }
        }
    }

    private void fireDeleteProjectEvent() {
        DeleteProjectEvent deleteProjectEvent = new DeleteProjectEvent();
        deleteProjectEvent.setToDoListGridWidget(this);

        eventBus.fireEvent(deleteProjectEvent);
    }
}
